﻿using Eps.Ai.Common;
using InfonavaEye.common;
using InfonavaEye.Handler;
using InfonavaEye.model;
using InfonavaEye.util;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace InfonavaEye.handler
{
    /// <summary>
    /// 自动抓拍处理类
    /// </summary>
    public class AutoCaptrueHandler
    {
        private static TaskFactory taskFactory = new TaskFactory(new LimitedTaskScheduler(1));
        AutoStartParams autoStartParams = null;

        /// <summary>
        /// 自动抓拍完成通知
        /// </summary>
        public event Action captureComplelteDelegate;

        //自动移动
        public delegate void NailAutoMovingDelegate(Nail nail);
        public event NailAutoMovingDelegate nailAutoMovingDelegate;

        //钉盒区域切换时产生在事件
        public delegate void BoxAreaSwitchDelegate(Nail nail);
        public event BoxAreaSwitchDelegate boxAreaSwitchDelegate;

        /// <summary>
        /// 钉盒模型
        /// </summary>
        private BoxModel boxModel = null;

        private AutoCaptureForm mainForm = null;

        private PLCHandler plcHandler;

        private CameraHandler cameraHandler;

        private LightHandler lightHandler = LightHandler.Instance;

        /// <summary>
        /// 已抓拍的钉子数量
        /// </summary>
        private int captruedNailCount = 0;

        //private Queue<CaptureTask> captureTaskQueue = new Queue<CaptureTask>();

        /// <summary>
        /// 当前走到的钉子（自动拍照）
        /// </summary>
        public static Nail currentNail = null;


        public static bool stoping = false;
        /// <summary>
        /// 是否暂停
        /// </summary>
        public static bool isPause = false;

        public AutoCaptrueHandler(AutoCaptureForm mainForm, PLCHandler plcHandler, CameraHandler cameraHandler)
        {
            this.mainForm = mainForm;
            this.plcHandler = plcHandler;
            this.cameraHandler = cameraHandler;
        }

        /// <summary>
        /// 开始
        /// </summary>
        /// <param name="autoStartParams"></param>
        public void start(AutoStartParams autoStartParams)
        {
            this.captruedNailCount = 0;
            this.autoStartParams = autoStartParams;
            this.boxModel = autoStartParams.BoxModel;
            if (this.boxModel == null) return;

            //启动线程
            this.autoStartParams.Stopwatch.Start();
            cameraHandler.UnWatchHeartBeat();
            plcHandler.lockManual();
            //光源
            if (this.boxModel.LightLuminosity==0) {
                this.boxModel.LightLuminosity = Config.LIGHT_SOURCE_LUMINOSITY_DEFAULT;
            }
            lightHandler.ChangeLuminance(this.boxModel.LightLuminosity);
            stoping = false;
            Thread demoThread = new Thread(new ThreadStart(walkByArea));
            demoThread.IsBackground = true;
            demoThread.Start();
        }


        /// <summary>
        /// 结束
        /// </summary>
        public void stop()
        {
            stoping = true;
            plcHandler.stopMoving();
            plcHandler.unlockManual();
            cameraHandler.WatchHeartBeat();
        }
        /// <summary>
        /// 暂停
        /// </summary>
        public void pause() {
            stoping = true;
            isPause = true;
            plcHandler.stopMoving();
        }
        public void resume() {
            stoping = false;
            isPause = false;
        }

        private int globalFixedX = 0;//校准位移X
        private int globalFixedY = 0;//校准位移Y
        XYZ[] fixZeroLocations; // 钉盒第一个区域-校准位置（x,y）
        /// <summary>
        ///  选择钉盒模板时选中的钉子的字典
        /// </summary>
        Dictionary<String, Nail> nailListToMap = null;

        float offsetX = 0;//x轴偏移
        float offsetY = 0;//y轴偏移
        float offsetZ = 0;//z轴偏移
        double offsetθ = 0;//角度偏移
        int[] startPoint = null;//起点

        /// <summary>
        /// 遍历每个区域
        /// </summary>
        private void walkByArea()
        {
            Console.WriteLine("开始自动抓拍。。。");
            bool success = true;
            int areaIndex = 0;
            FormDataBridge.autoCaptureNailQueue.Clear();

            if (this.boxModel == null || this.boxModel.boxAreas == null || this.boxModel.boxAreas.Count == 0)
            {
                MessageBox.Show("钉盒模板有问题,无法计算钉子的位置");
                return;
            }

            //通过原点校准的第一颗钉子的坐标计算实际偏差位移
            var firstArea = this.boxModel.boxAreas[0];
            fixZeroLocations = this.autoStartParams.FixZeroLocations;
            nailListToMap = this.autoStartParams.TemplateNailMap;
            //二维坐标平移旋转计算 参考：https://www.cnblogs.com/softhal/p/5648463.html
            //https://www.cnblogs.com/softhal/p/5648463.html
            // (x',y')为校准的点，（x,y）为校准前的点，
            // θ为 2个坐标系的对角线的角度差
            // a 为2个坐标系原点的x坐标的偏移
            // b 为2个坐标系原点的y坐标的偏移
            //x'=x·cos(θ)+y·sin(θ)+a
            //y'=y·cos(θ)-x·sin(θ)+b

            //校准前2个点的坐标
            var boxAngle = this.boxModel.boxAngle;
            var bitch = firstArea.holdPitch;
            startPoint = firstArea.startPoint;
            var x1 = firstArea.startPoint[0];//校准前坐标系起点(右下角)的x
            var y1 = firstArea.startPoint[1];//校准前坐标系起点(右下角)的y
            var x2 = (int)(firstArea.startPoint[0] + bitch[0] * (firstArea.columnCount - 1) * Math.Cos(boxAngle * Math.PI / 180));//校准前区域(左下角)的x
            var y2 = (int)(firstArea.startPoint[1] + bitch[0] * (firstArea.columnCount - 1) * Math.Sin(boxAngle * Math.PI / 180));//校准前区域(左下角)的y

            //校准后2个点的坐标
            var x3 = fixZeroLocations[0].X;//校准后坐标系起点的x
            var y3 = fixZeroLocations[0].Y;//校准后坐标系起点的y
            var x4 = fixZeroLocations[2].X;//校准后区域左下角的x
            var y4 = fixZeroLocations[2].Y;//校准后区域左下角的y

            //计算2个坐标系旋转的角度（角度差）
            var θ1 = boxAngle;//校准前的左下角钉子的角度
            var θ2 = MathUtil.anglevalue(x3, y3, x4, y4);//校准后的左下角钉子的角度
            this.offsetθ = θ2 - θ1;
            Console.WriteLine("2个坐标系旋转的角度（角度差）θ= {0}", this.offsetθ);


            //计算a和b (原点偏差)
            var a = x3 - x1;
            var b = y3 - y1;
            this.offsetX = a;//x轴偏移 = a
            this.offsetY = b;//y轴偏移 = b
            this.offsetZ =  fixZeroLocations[0].Z - firstArea.startPoint[2];
            Console.WriteLine("原点校准,偏差位移 offsetX {0}, offsetY {1}", this.offsetX, this.offsetY);

            //设定速度
            plcHandler.ChangeSpeed(this.boxModel.SpeedX,this.boxModel.SpeedY,Config.MOTOR_DEFAULT_SPEED);

            //captureTaskQueue.Clear();
            this.boxModel.boxAreas.ForEach((area) =>
            {
                if (stoping)
                {
                    Console.WriteLine("强制停止自动抓拍.");
                    return;
                }
                Console.WriteLine("当前区域: {0},{1}", area.code, area.name);
                try
                {
                    int lastRow = 0;
                    int lastColumn = 0;
                    //开始走x，y矩阵
                    for (int x = 0; x < area.columnCount; x++)
                    {
                        if (stoping)
                        {
                            Console.WriteLine("强制停止自动抓拍.");
                            return;
                        }

                        if (x % 2 == 0)
                        {
                            //偶数行 正走
                            for (int y = 0; y < area.rowCount; y++)
                            {
                                this.addCaptureTask(area, x, y);
                            }
                        }
                        else
                        {
                            //奇数行 反走
                            for (int y = area.rowCount - 1; y >= 0; y--)
                            {
                                this.addCaptureTask(area, x, y);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    success = false;
                    Console.WriteLine("自动抓拍发生异常，{0}，{1}", ex.Message, ex.StackTrace);
                    MessageBox.Show("自动抓拍发生异常，" + ex.Message);
                }
                areaIndex++;
            });
            //开始队列中的第一个任务
            FormDataBridge.odlImageOCrResult = null;
            nailCapture(true);
            //（下一个任务是在mainForm中收到抓拍成功的通知后触发的）
        }
        public void nailCapture() {
            nailCapture(false);
        }
        /// <summary>
        /// 拍照
        /// </summary>
        public void nailCapture(bool firstCapture)
        {
            if (FormDataBridge.autoCaptureNailQueue.Count > 0)
            {
                taskFactory.StartNew(() =>
                {
                    // Task.Delay(500);
                    var task = FormDataBridge.autoCaptureNailQueue.Peek();
                    if (!firstCapture&&!currentNail.area.code.Equals(task.area.code) && this.boxModel.spanAreaPause) {
                        boxAreaSwitchDelegate.Invoke(task);
                    }
                    else
                    {
                        var xyz = task.actualXyz;
                        var currentArea = task.area;
                        //luoxuebo 每个区域不同的光源亮度所以在运行时需要根据模板区域设置的亮度设置光源亮度
                        if (currentArea.lightLuminosity != this.boxModel.LightLuminosity)
                        {
                            this.boxModel.LightLuminosity = currentArea.lightLuminosity;
                            lightHandler.ChangeLuminance(this.boxModel.LightLuminosity);
                        }
                        if ((currentNail == null && currentArea.autoShotMagnification > 0) || (currentNail!=null&&currentNail.area.autoShotMagnification != currentArea.autoShotMagnification))
                        {
                            AutoShotHelper.Instance.sendMsg(currentArea.autoShotMagnification.ToString());
                        }
                        plcHandler.moveToXYZandCapture(xyz.X, xyz.Y, xyz.Z,xyz.ZIlluminant, false);
                        this.captruedNailCount++;
                        if (nailAutoMovingDelegate != null)
                        {
                            nailAutoMovingDelegate.Invoke(task);
                        }
                        currentNail = task;
                        /* updateNailStatus(new OCRResult()
                       {
                           nail = nail,
                       });*/
                        cameraHandler.StartCapture();
                    }
                   
                });
            }
            else
            {
                if (captureComplelteDelegate != null)
                {
                    captureComplelteDelegate.Invoke();
                }
                plcHandler.unlockManual();
                cameraHandler.UnWatchHeartBeat();
                var stopWatch = this.autoStartParams.Stopwatch;
                stopWatch.Stop();
                TimeSpan ts = stopWatch.Elapsed;
                // 格式化并显示TimeSpan值.
                string elapsedTime = String.Format("{0:00}分{1:00}秒",ts.Minutes, ts.Seconds);
                //节省时间:创伤钉子按照8S，脊柱钉一般5S做对比
                var totalCutSecond = this.captruedNailCount * (this.autoStartParams.BoxModel.NailShape == 2 ? 8: 5);
                var totalCutTime = String.Format("{0}分{1}秒", totalCutSecond/60, totalCutSecond%60);
                //累积节约时间
                Config.APP_SAVE_SECONDS += totalCutSecond;
                Config.INI.WriteValue("device", "APP_SAVE_SECONDS", Config.APP_SAVE_SECONDS.ToString());

                MessageBox.Show(String.Format("自动识别已完成，用时{0}，为您节省{1}",elapsedTime,totalCutTime));
            }
        }
       
        private void addCaptureTask(BoxArea area, int xIndex, int yIndex)
        {
            var nailKey = string.Format(AutoStartParams.NAIL_MAP_KEY_FORMAT, area.code, xIndex, yIndex);
            if (nailListToMap == null || !nailListToMap.ContainsKey(nailKey) || nailListToMap[nailKey].status != NailStatus.Selected)
            {
                Console.WriteLine("跳过钉子，因为nailListToMap中不存在key:" + nailKey);
                return;
            }

            var boxAngle = this.boxModel.boxAngle;//建模时的斜度
            var startPoint = area.startPoint;
            int[] holdPitch = area.holdPitch;
            //这里需要根据斜边算出当前钉子的x和y.
            int Dx = holdPitch[0] * xIndex;//x方向的距离
            int Dy = holdPitch[1] * yIndex;//y方向的距离
            //计算仅延y轴的移动y轴孔距后的第一颗钉子的位置
            int x1 = startPoint[0] - (int)(Dy * Math.Sin(boxAngle * Math.PI / 180));
            int y1 = startPoint[1] + (int)(Dy * Math.Cos(boxAngle * Math.PI / 180));
            //继续计算延x轴移动x轴孔距后的位置
            int x = x1 + (int)(Dx * Math.Cos(boxAngle * Math.PI / 180));
            int y = y1 + (int)(Dx * Math.Sin(boxAngle * Math.PI / 180));
            int z = startPoint[2];//z代表焦距
           
            Console.WriteLine("当前钉子XY坐标: x {0}, y {1}", xIndex, yIndex);
            Console.WriteLine("计算出的XYZ绝对位置，x {0},y {1},z {2}", x, y, z);

            //二维坐标平移旋转计算 参考：https://www.cnblogs.com/softhal/p/5648463.html
            //https://www.cnblogs.com/softhal/p/5648463.html
            // (x',y')为校准的点，（x,y）为校准前的点，
            // θ为 2个坐标系的对角线的角度差
            // a 为2个坐标系原点的x坐标的偏移
            // b 为2个坐标系原点的y坐标的偏移
            //x'=x·cos(θ)+y·sin(θ)+a
            //y'=y·cos(θ)-x·sin(θ)+b

            /*  var newX = x * Math.Cos(offsetθ) + y * Math.Sin(offsetθ) + offsetX;
              var newY = y * Math.Cos(offsetθ) - x * Math.Sin(offsetθ) + offsetY;
              */
            Matrix myMatrix = new Matrix();
            myMatrix.Translate(0 - startPoint[0], 0 - startPoint[1]);//平移到新的坐标系
            myMatrix.Rotate((float)offsetθ, MatrixOrder.Append);//旋转角度
            myMatrix.Translate(startPoint[0], startPoint[1], MatrixOrder.Append);//平移回到原来的坐标系
            var array = new System.Drawing.PointF[] {
                new System.Drawing.PointF(x, y),
            };
            myMatrix.TransformPoints(array);

            var actualXyz = new XYZ()
            {
                X = (int)(array[0].X + this.offsetX),
                Y = (int)(array[0].Y + this.offsetY),
                Z = z + (int)this.offsetZ,
                ZIlluminant = area.startPoint.Length < 4 ? 0 : area.startPoint[3] //光源轴
            };
            currentNail = new Nail()
            {
                boxModel = this.boxModel,
                area = area,
                location = new System.Drawing.Rectangle(x, y, 0, 0),
                point = new System.Drawing.Point(xIndex, yIndex),
                actualXyz = actualXyz,
            };

            FormDataBridge.autoCaptureNailQueue.Enqueue(currentNail);

            /*if (nailAutoMovingDelegate!=null) {
                nailAutoMovingDelegate.Invoke(currentNail);
            }*/

            /* captureTaskQueue.Enqueue(new CaptureTask()
             {
                 Xyz = actualXyz,
             });*/
        }
    }
}
